// SPDX-FileCopyrightText: Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
// SPDX-License-Identifier: BSD-3-Clause
#include "vtktransformwidget.h"

#include "vtkActor.h"
#include "vtkAssemblyNode.h"
#include "vtkAssemblyPath.h"
#include "vtkCallbackCommand.h"
#include "vtkCamera.h"
#include "vtkCellPicker.h"
#include "vtkMath.h"
#include "vtkObjectFactory.h"
#include "vtkPickingManager.h"
#include "vtkPolyDataMapper.h"
#include "vtkProperty.h"
#include "vtkRenderWindowInteractor.h"
#include "vtkRenderer.h"
#include "vtkTransform.h"
#include "vtkProp3DCollection.h"
#include <vtkAssembly.h>
#include <vtkCursor3D.h>
#include <vtkArrowSource.h>
#include <vtkRegularPolygonSource.h>

VTK_ABI_NAMESPACE_BEGIN
    vtkStandardNewMacro(MyBoxWidget);

Arrow_t::Arrow_t() :
    Source(vtkArrowSource::New()),
    Mapper(vtkPolyDataMapper::New()),
    Actor(vtkActor::New())
{
    Source->SetShaftRadius(0.01);
    Source->SetTipRadius(0.1);

    Mapper->SetInputConnection(Source->GetOutputPort());
    Actor->SetMapper(Mapper);
}

Arrow_t::~Arrow_t()
{
    this->Source->Delete();
    this->Mapper->Delete();
    this->Actor->Delete();
}

Circle_t::Circle_t() :
    Source(vtkRegularPolygonSource::New()),
    Mapper(vtkPolyDataMapper::New()),
    Actor(vtkActor::New())
{
    Source->GeneratePolygonOff();
    Source->SetNumberOfSides(50);
    Source->SetRadius(3);
    Source->SetCenter(0, 0, 0);

    Mapper->SetInputConnection(Source->GetOutputPort());
    Actor->SetMapper(Mapper);

    // Actor->GetProperty()->EdgeVisibilityOn();
    Actor->GetProperty()->SetLineWidth(10.0);
    Actor->GetProperty()->SetRepresentationToWireframe();
}

Circle_t::~Circle_t()
{
    this->Source->Delete();
    this->Mapper->Delete();
    this->Actor->Delete();
}


MyBoxWidget::MyBoxWidget():
    Arrows(),
    Circles()
{
    this->State = MyBoxWidget::Start;
    this->EventCallbackCommand->SetCallback(MyBoxWidget::ProcessEvents);

    // Enable/disable the translation, rotation, and scaling of the widget
    this->TranslationEnabled = 1;
    this->RotationEnabled = 1;
    this->ScalingEnabled = 1;

    // Build the representation of the widget
    int i;

    ///@{创建所有角色
    /// 创建点角色
    this->Cursor3D = vtkCursor3D::New();
    this->Cursor3D->AllOff();
    this->Cursor3D->AxesOn();
    this->Cursor3D->TranslationModeOn();

    this->Mapper = vtkPolyDataMapper::New();
    this->Mapper->SetInputConnection(this->Cursor3D->GetOutputPort());

    this->Actor = vtkActor::New();
    this->Actor->SetMapper(this->Mapper);

    ///创建位移手柄
    auto& [xArrow,yArrow,zArrow] {Arrows};

    xArrow.Actor->GetProperty()->SetColor(1.0, 0.0, 0.0);
    xArrow.Actor->SetPosition(0.5, 0.0, 0.0);
    xArrow.Actor->SetOrientation(90, 0, 0);

    yArrow.Actor->GetProperty()->SetColor(0.0, 1.0, 0.0);
    yArrow.Actor->SetPosition(0.0, 0.5, 0.0);
    yArrow.Actor->SetOrientation(0, 0, 90);

    zArrow.Actor->GetProperty()->SetColor(0.0, 0.0, 1.0);
    zArrow.Actor->SetPosition(0.0, 0.0, -0.5);
    zArrow.Actor->SetOrientation(0, 90, 0);

    ///创建旋转手柄
    auto& [xCircle,yCircle,zCircle] {Circles};

    xCircle.Actor->GetProperty()->SetColor(1.0, 0.0, 0.0);
    xCircle.Actor->SetOrientation(90, 0, 0);

    yCircle.Actor->GetProperty()->SetColor(0.0, 1.0, 0.0);
    yCircle.Actor->SetOrientation(0, 0, 90);

    yCircle.Actor->GetProperty()->SetColor(0.0, 0.0, 1.0);
    yCircle.Actor->SetOrientation(0, 90, 0);
    ///@}

    //添加所有角色到组
    this->Assembly = vtkAssembly::New();
    this->Assembly->AddPart(xArrow.Actor);
    this->Assembly->AddPart(yArrow.Actor);
    this->Assembly->AddPart(zArrow.Actor);
    this->Assembly->AddPart(xCircle.Actor);
    this->Assembly->AddPart(yCircle.Actor);
    this->Assembly->AddPart(zCircle.Actor);
    this->Assembly->AddPart(Actor);

    // Points 8-14 are down by PositionHandles();
    // this->PlaceWidget(Assembly->GetBounds());

    // Manage the picking stuff
    this->TranlatePicker = vtkCellPicker::New();
    this->TranlatePicker->SetTolerance(0.01);
    this->TranlatePicker->AddPickList(this->Actor);
    this->TranlatePicker->AddPickList(xArrow.Actor);
    this->TranlatePicker->AddPickList(yArrow.Actor);
    this->TranlatePicker->AddPickList(zArrow.Actor);
    this->TranlatePicker->PickFromListOn();

    this->RotatePicker = vtkCellPicker::New();
    this->RotatePicker->SetTolerance(0.01);
    this->RotatePicker->AddPickList(xCircle.Actor);
    this->RotatePicker->AddPickList(yCircle.Actor);
    this->RotatePicker->AddPickList(zCircle.Actor);
    this->RotatePicker->PickFromListOn();

    this->CurHandle = SelectHandle::NoneSelect;

    this->Transform = vtkTransform::New();
    // this->Assembly->SetUserTransform(this->Transform);
    xArrow.Actor->SetUserTransform(this->Transform);
    yArrow.Actor->SetUserTransform(this->Transform);
    zArrow.Actor->SetUserTransform(this->Transform);
    xCircle.Actor->SetUserTransform(this->Transform);
    yCircle.Actor->SetUserTransform(this->Transform);
    zCircle.Actor->SetUserTransform(this->Transform);
    this->Actor->SetUserTransform(this->Transform);

    this->RotateTransform = vtkTransform::New();
    this->OriginMatrix = vtkMatrix4x4::New();

    // Set up the initial properties
    this->CreateDefaultProperties();
}

MyBoxWidget::~MyBoxWidget()
{
    this->TranlatePicker->Delete();
    this->RotatePicker->Delete();
    this->Transform->Delete();
    this->RotateTransform->Delete();
    this->OriginMatrix->Delete();
    this->Cursor3D->Delete();
    this->Mapper->Delete();
    this->Actor->Delete();
    this->Assembly->Delete();
    this->TranslateProperty->Delete();
    this->RotationProperty->Delete();
    this->CenterProperty->Delete();
}

void MyBoxWidget::CreateDefaultProperties()
{
    TranslateProperty = vtkProperty::New();
    RotationProperty = vtkProperty::New();
    CenterProperty =vtkProperty::New();
}

void MyBoxWidget::SetEnabled(int enabling)
{
    if (!this->Interactor)
    {
        vtkErrorMacro(<< "The interactor must be set prior to enabling/disabling widget");
        return;
    }

    if (enabling) //------------------------------------------------------------
    {
        vtkDebugMacro(<< "Enabling widget");

        if (this->Enabled) // already enabled, just return
        {
            return;
        }

        if (!this->CurrentRenderer)
        {
            this->SetCurrentRenderer(this->Interactor->FindPokedRenderer(
                this->Interactor->GetLastEventPosition()[0], this->Interactor->GetLastEventPosition()[1]));
            if (this->CurrentRenderer == nullptr)
            {
                return;
            }
        }

        this->Enabled = 1;

        // listen to the following events
        vtkRenderWindowInteractor* i = this->Interactor;
        i->AddObserver(vtkCommand::MouseMoveEvent, this->EventCallbackCommand, this->Priority);
        i->AddObserver(vtkCommand::LeftButtonPressEvent, this->EventCallbackCommand, this->Priority);
        i->AddObserver(vtkCommand::LeftButtonReleaseEvent, this->EventCallbackCommand, this->Priority);

        // Add the various actors
        // Add the outline

        auto& [xArrow,yArrow,zArrow] {Arrows};
        auto& [xCircle,yCircle,zCircle] {Circles};

        this->CurrentRenderer->AddActor(this->Assembly);

        this->RegisterPickers();

        this->InvokeEvent(vtkCommand::EnableEvent, nullptr);
    }

    else // disabling-------------------------------------------------------------
    {
        vtkDebugMacro(<< "Disabling widget");

        if (!this->Enabled) // already disabled, just return
        {
            return;
        }

        this->Enabled = 0;

        // don't listen for events any more
        this->Interactor->RemoveObserver(this->EventCallbackCommand);

        // turn off the outline
        auto& [xArrow,yArrow,zArrow] {Arrows};
        auto& [xCircle,yCircle,zCircle] {Circles};

        this->CurrentRenderer->RemoveActor(this->Assembly);
        // this->CurrentRenderer->RemoveActor(xArrow.Actor);
        // this->CurrentRenderer->RemoveActor(yArrow.Actor);
        // this->CurrentRenderer->RemoveActor(zArrow.Actor);
        // this->CurrentRenderer->RemoveActor(xCircle.Actor);
        // this->CurrentRenderer->RemoveActor(yCircle.Actor);
        // this->CurrentRenderer->RemoveActor(zCircle.Actor);

        this->CurHandle = SelectHandle::NoneSelect;
        this->InvokeEvent(vtkCommand::DisableEvent, nullptr);
        this->SetCurrentRenderer(nullptr);
        this->UnRegisterPickers();
    }

    this->Interactor->Render();
}

void MyBoxWidget::PlaceWidget(double bounds[6])
{
    int i;
    double center[3],newPos[3];

    center[0] = (bounds[0] + bounds[1]) / 2.0; // X 坐标
    center[1] = (bounds[2] + bounds[3]) / 2.0; // Y 坐标
    center[2] = (bounds[4] + bounds[5]) / 2.0; // Z 坐标

    auto* parts = this->Assembly->GetParts();
    vtkProp3D* it = parts->GetNextProp3D();
    while (it != nullptr)
    {
        auto* pos = it->GetPosition();
        newPos[0] = pos[0] + center[0];
        newPos[1] = pos[1] + center[1];
        newPos[2] = pos[2] + center[2];

        it->SetPosition(newPos);

        it = parts->GetNextProp3D();
    }

    this->Transform->Translate(center);

    this->Transform->GetMatrix(this->OriginMatrix);
    // this->Assembly->SetPosition(center);
}

void MyBoxWidget::ProcessEvents(
    vtkObject* vtkNotUsed(object), unsigned long event, void* clientdata, void* vtkNotUsed(calldata))
{
    MyBoxWidget* self = reinterpret_cast<MyBoxWidget*>(clientdata);

    // okay, let's do the right thing
    switch (event)
    {
    case vtkCommand::LeftButtonPressEvent:
        self->OnLeftButtonDown();
        break;
    case vtkCommand::LeftButtonReleaseEvent:
        self->OnLeftButtonUp();
        break;
    case vtkCommand::MouseMoveEvent:
        self->OnMouseMove();
        break;
    }
}

void MyBoxWidget::PrintSelf(ostream& os, vtkIndent indent)
{
    this->Superclass::PrintSelf(os, indent);

    double* bounds = this->InitialBounds;
    os << indent << "Initial Bounds: "
       << "(" << bounds[0] << "," << bounds[1] << ") "
       << "(" << bounds[2] << "," << bounds[3] << ") "
       << "(" << bounds[4] << "," << bounds[5] << ")\n";

    if (this->TranslateProperty)
    {
        os << indent << "Translate Handle Property: " << this->TranslateProperty << "\n";
    }
    else
    {
        os << indent << "TranslateHandle Property: (none)\n";
    }
    if (this->RotationProperty)
    {
        os << indent << "Rotation Handle Property: " << this->RotationProperty << "\n";
    }
    else
    {
        os << indent << "RotationHandle Property: (none)\n";
    }

    if (this->CenterProperty)
    {
        os << indent << "Center Property: " << this->CenterProperty << "\n";
    }
    else
    {
        os << indent << "Center Property: (none)\n";
    }

    os << indent << "Translation Enabled: " << (this->TranslationEnabled ? "On\n" : "Off\n");
    os << indent << "Scaling Enabled: " << (this->ScalingEnabled ? "On\n" : "Off\n");
    os << indent << "Rotation Enabled: " << (this->RotationEnabled ? "On\n" : "Off\n");
}

void MyBoxWidget::TranslateHandlesOn()
{
    auto& [xArrow,yArrow,zArrow] {Arrows};

    xArrow.Actor->VisibilityOn();
    yArrow.Actor->VisibilityOn();
    zArrow.Actor->VisibilityOn();
}

void MyBoxWidget::TranslateHandlesOff()
{
    auto& [xArrow,yArrow,zArrow] {Arrows};

    xArrow.Actor->VisibilityOff();
    yArrow.Actor->VisibilityOff();
    zArrow.Actor->VisibilityOff();
}

void MyBoxWidget::RotationHandlesOn()
{
    auto& [xCircle,yCircle,zCircle] {Circles};

    xCircle.Actor->VisibilityOn();
    yCircle.Actor->VisibilityOn();
    zCircle.Actor->VisibilityOn();
}

void MyBoxWidget::RotationHandlesOff()
{
    auto& [xCircle,yCircle,zCircle] {Circles};

    xCircle.Actor->VisibilityOff();
    yCircle.Actor->VisibilityOff();
    zCircle.Actor->VisibilityOff();
}

void MyBoxWidget::OnLeftButtonDown()
{
    int X = this->Interactor->GetEventPosition()[0];
    int Y = this->Interactor->GetEventPosition()[1];

    // Okay, we can process this. Try to pick handles first;
    // if no handles picked, then pick the bounding box.
    if (!this->CurrentRenderer || !this->CurrentRenderer->IsInViewport(X, Y))
    {
        this->State = MyBoxWidget::Outside;
        return;
    }

    vtkAssemblyPath* path = this->GetAssemblyPath(X, Y, 0., this->TranlatePicker);

    this->State = MyBoxWidget::Outside;
    this->CurHandle = SelectHandle::NoneSelect;

    if (path != nullptr)
    {
        this->State = MyBoxWidget::Moving;
        this->TranlatePicker->GetPickPosition(this->LastPickPosition);
        this->ValidPick = 1;

        auto* currentHandle = static_cast<vtkActor*>(path->GetFirstNode()->GetViewProp());

        for (int i = 0; i < this->Arrows.size(); ++i) {
            if(this->Arrows[i].Actor == currentHandle)
            {
                this->Arrows[i].Actor->GetProperty()->DeepCopy(this->TranslateProperty);
                this->Arrows[i].Actor->GetProperty()->SetColor(1.,1.,1.);
                this->CurHandle = i;
            }
        }

        if(this->Actor == currentHandle)
        {
            this->Actor->GetProperty()->DeepCopy(this->TranslateProperty);
            this->Actor->GetProperty()->SetColor(1.,0.,0.);
            this->CurHandle = SelectHandle::Cursor;
        }

    }
    else
    {
        path = this->GetAssemblyPath(X, Y, 0., this->RotatePicker);

        if (path != nullptr)
        {
            this->State = MyBoxWidget::Rotation;
            this->RotatePicker->GetPickPosition(this->LastPickPosition);
            this->ValidPick = 1;

            auto* currentHandle = static_cast<vtkActor*>(path->GetFirstNode()->GetViewProp());
            for (int i = 0; i < this->Circles.size(); ++i) {
                if(this->Circles[i].Actor == currentHandle)
                    this->CurHandle = i+3;
            }
        }
        else
            return;
    }

    this->EventCallbackCommand->SetAbortFlag(1);
    this->StartInteraction();
    this->InvokeEvent(vtkCommand::StartInteractionEvent, nullptr);
    this->Interactor->Render();
}

void MyBoxWidget::OnLeftButtonUp()
{
    if (this->State == MyBoxWidget::Outside || this->State == MyBoxWidget::Start)
    {
        return;
    }

    this->State = MyBoxWidget::Start;
    // this->SizeHandles();

    this->EventCallbackCommand->SetAbortFlag(1);
    this->EndInteraction();
    this->InvokeEvent(vtkCommand::EndInteractionEvent, nullptr);
    this->Interactor->Render();
}

void MyBoxWidget::OnMouseMove()
{
    // See whether we're active
    if (this->State == MyBoxWidget::Outside || this->State == MyBoxWidget::Start)
    {
        return;
    }

    int X = this->Interactor->GetEventPosition()[0];
    int Y = this->Interactor->GetEventPosition()[1];

    double focalPoint[4], pickPoint[4], prevPickPoint[4];
    double z, vpn[3];

    vtkCamera* camera = this->CurrentRenderer->GetActiveCamera();
    if (!camera)
    {
        return;
    }

    // Compute the two points defining the motion vector
    this->ComputeWorldToDisplay(this->LastPickPosition[0],
                                this->LastPickPosition[1],
                                this->LastPickPosition[2],
                                focalPoint);
    z = focalPoint[2];

    this->ComputeDisplayToWorld(double(this->Interactor->GetLastEventPosition()[0]),
                                double(this->Interactor->GetLastEventPosition()[1]),
                                z,
                                prevPickPoint);

    this->ComputeDisplayToWorld(double(X), double(Y), z, pickPoint);

    //计算差值
    double v[3];

    v[0] = pickPoint[0] - prevPickPoint[0];
    v[1] = pickPoint[1] - prevPickPoint[1];
    v[2] = pickPoint[2] - prevPickPoint[2];

    // Process the motion
    switch (this->CurHandle) {
    case SelectHandle::XTranslate:
    {
        v[1] = 0.0 , v[2] = 0.0;
        this->Translate(v);
        break;
    }
    case SelectHandle::YTranslate:
    {
        v[0] = 0.0 , v[2] = 0.0;
        this->Translate(v);
        break;
    }
    case SelectHandle::ZTranslate:
    {
        v[0] = 0.0 , v[1] = 0.0;
        this->Translate(v);
        break;
    }
    case SelectHandle::XRotation:
    {
        double arg[3]{0.0,1.0,0.0};

        camera->GetViewPlaneNormal(vpn);
        this->Rotate(X, Y, prevPickPoint, pickPoint, vpn,arg);
        break;
    }
    case SelectHandle::YRotation:
    {
        double arg[3]{1.0,0.0,0.0};

        camera->GetViewPlaneNormal(vpn);
        this->Rotate(X, Y, prevPickPoint, pickPoint, vpn,arg);
        break;
    }
    case SelectHandle::ZRotation:
    {
        double arg[3]{0.0,0.0,1.0};

        camera->GetViewPlaneNormal(vpn);
        this->Rotate(X, Y, prevPickPoint, pickPoint, vpn,arg);
        break;
    }
    case SelectHandle::Cursor:
    {
        this->Translate(v);
        break;
    }
    default:
        return;
        break;
    }

    // Interact, if desired
    this->EventCallbackCommand->SetAbortFlag(1);
    this->InvokeEvent(vtkCommand::InteractionEvent, nullptr);
    this->Interactor->Render();
}

// Loop through all points and translate them
void MyBoxWidget::Translate(double* v)
{
    this->Transform->PostMultiply(); // 确保变换是按顺序应用的
    this->Transform->Translate(v);

    // 将变换应用到演员
    // this->Assembly->SetUserTransform(transform);
}

void MyBoxWidget::Rotate(int X, int Y, double* p1, double* p2, double* vpn,double* arg)
{
    double v[3];    // vector of motion
    double axis[3]; // axis of rotation
    double theta;   // rotation angle

    v[0] = p2[0] - p1[0];
    v[1] = p2[1] - p1[1];
    v[2] = p2[2] - p1[2];

    // Create axis of rotation and angle of rotation
    vtkMath::Cross(vpn, v, axis);
    if (vtkMath::Normalize(axis) == 0.0)
    {
        return;
    }
    const int* size = this->CurrentRenderer->GetSize();
    double l2 = (X - this->Interactor->GetLastEventPosition()[0]) *
                    (X - this->Interactor->GetLastEventPosition()[0]) +
                (Y - this->Interactor->GetLastEventPosition()[1]) *
                    (Y - this->Interactor->GetLastEventPosition()[1]);
    theta = 360.0 * sqrt(l2 / (size[0] * size[0] + size[1] * size[1]));


    vtkSmartPointer<vtkTransform> transform = vtkSmartPointer<vtkTransform>::New();
    transform->PostMultiply(); // 确保变换是按顺序应用的

    // Manipulate the transform to reflect the rotation
    // Transform->Identity();
    // this->Transform->Translate(center[0], center[1], center[2]);
    double rec[3];
    rec[0] = axis[0] * arg[0];
    rec[1] = axis[1] * arg[1];
    rec[2] = axis[2] * arg[2];
    this->RotateTransform->RotateWXYZ(theta, rec);
    // this->Transform->Translate(-center[0], -center[1], -center[2]);
}

void MyBoxWidget::GetTransform(vtkTransform* t)
{
    t->Identity();

    // 获取源变换的矩阵
    vtkSmartPointer<vtkMatrix4x4> matrix = this->Transform->GetMatrix();

    // 计算第一次变换的逆矩阵
    vtkSmartPointer<vtkMatrix4x4> inverseMatrix1 = vtkSmartPointer<vtkMatrix4x4>::New();
    vtkMatrix4x4::Invert(this->OriginMatrix, inverseMatrix1);

    // 计算相对变换矩阵：M_relative = M1^{-1} * M2
    vtkSmartPointer<vtkMatrix4x4> relativeMatrix = vtkSmartPointer<vtkMatrix4x4>::New();
    vtkMatrix4x4::Multiply4x4(inverseMatrix1, matrix, relativeMatrix);

    t->SetMatrix(relativeMatrix);
}

void MyBoxWidget::GetTransformOfRotate(vtkTransform *t)
{
    t->Identity();

    // 获取源变换的矩阵
    vtkSmartPointer<vtkMatrix4x4> matrix = this->RotateTransform->GetMatrix();
    t->SetMatrix(matrix);
}

void MyBoxWidget::SetTransform(vtkTransform* t)
{
    t->DeepCopy(this->Transform);
}

//------------------------------------------------------------------------------
void MyBoxWidget::RegisterPickers()
{
    vtkPickingManager* pm = this->GetPickingManager();
    if (!pm)
    {
        return;
    }
    pm->AddPicker(this->TranlatePicker, this);
    pm->AddPicker(this->RotatePicker, this);
}
VTK_ABI_NAMESPACE_END
